/* ----------------------------------------------------------------------------
 * Copyright (c) Huawei Technologies Co., Ltd. 2013-2019. All rights reserved.
 * Description: LiteOS Task Module Implementation
 * Author: liuzhibin
 * Create: 2013-01-01
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 * --------------------------------------------------------------------------- */

#include "los_hwi.h"
#include "los_tick_pri.h"
#include "hisoc/timer.h"
#include "time.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#ifdef LOSCFG_SHELL_EXCINFO
#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR
#include "linux/mtd/mtd.h"
#include "linux/module.h"
#include "linux/mtd/mtd_list.h"
#endif

static log_read_write_fn g_excInfoRW = NULL;  // the hook of   read-writing exception information
static CHAR *g_excInfoBuf = NULL;  // pointer  to the buffer for storing the exception information
static UINT32 g_excInfoIndex = 0xffff;  // the index of the buffer  for storing the exception information
static UINT32 g_recordAddr = 0;  // the address  of  storing the exception information
static UINT32 g_recordSpace = 0;  // the size of  storing the exception information

void SetExcInfoRW(log_read_write_fn func)
{
    g_excInfoRW = func;
}

log_read_write_fn GetExcInfoRW(void)
{
    return g_excInfoRW;
}

void SetExcInfoBuf(CHAR *buf)
{
    g_excInfoBuf = buf;
}

CHAR *GetExcInfoBuf(void)
{
    return g_excInfoBuf;
}

void SetExcInfoIndex(UINT32 index)
{
    g_excInfoIndex = index;
}

UINT32 GetExcInfoIndex(void)
{
    return g_excInfoIndex;
}

void SetRecordAddr(UINT32 addr)
{
    g_recordAddr = addr;
}

UINT32 GetRecordAddr(void)
{
    return g_recordAddr;
}

void SetRecordSpace(UINT32 space)
{
    g_recordSpace = space;
}

UINT32 GetRecordSpace(void)
{
    return g_recordSpace;
}

void WriteExcInfoToBuf(const char *format, ...)
{
    if (g_recordSpace > g_excInfoIndex) {
        va_list arglist;

        va_start(arglist, format);
        g_excInfoIndex += vsnprintf_s((g_excInfoBuf + g_excInfoIndex), (g_recordSpace - g_excInfoIndex),
                                      (g_recordSpace - g_excInfoIndex - 1), format, arglist);
        va_end(arglist);
        (void)arglist; /* to clear e438 last value assigned not used , the compiler will optimize this code */
    }
}

#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR
#include "spinor.h"

static struct mtd_info *g_mtdSpinor = NULL;

static void OsSpiflashErase(size_t start, size_t size)
{
    struct erase_info eraseInfo;

    (VOID)memset_s(&eraseInfo, sizeof(struct erase_info), 0, sizeof(struct erase_info));
    eraseInfo.mtd = g_mtdSpinor;
    eraseInfo.callback = NULL; /*lint !e64*/
    eraseInfo.fail_addr = (uint64_t)MTD_FAIL_ADDR_UNKNOWN;
    eraseInfo.addr = start;
    eraseInfo.len = size;
    eraseInfo.time = 1;
    eraseInfo.retries = 1;
    eraseInfo.dev = 0;
    eraseInfo.cell = 0;
    eraseInfo.priv = 0;
    eraseInfo.state = 0;
    eraseInfo.next = (struct erase_info *)NULL;
    eraseInfo.scrub = 0;
    (void)g_mtdSpinor->erase(g_mtdSpinor, &eraseInfo);
}

static int OsWriteExcInfoToSpiFlush(UINT32 startAddr, UINT32 space, VOID *buf)
{
    UINT32 outLen;
    UINT32 len = (space < g_recordSpace) ? space : g_recordSpace;
    int ret;

    OsSpiflashErase(g_recordAddr, LOS_Align(g_recordSpace, g_mtdSpinor->erasesize));
    ret = g_mtdSpinor->write(g_mtdSpinor, g_recordAddr, len, &outLen, (const char *)buf);
    return ret;
}

static int OsReadExcInfoForSpiFlush(UINT32 startAddr, UINT32 space, VOID *buf)
{
    UINT32 outLen;
    UINT32 len = (space < g_recordSpace) ? space : g_recordSpace;
    int ret;

    ret = g_mtdSpinor->read(g_mtdSpinor, g_recordAddr, len, &outLen, (char *)buf);
    return ret;
}
#endif
#endif

static volatile unsigned long long g_schedClockNanosec = 0;
static volatile unsigned long long g_schedClockCycle = 0;
static volatile unsigned long g_timeClkLast = 0;

static unsigned long UpdateTimeClk(unsigned long *timeClk32)
{
    unsigned long timeClkNow;

    timeClkNow = time_clk_read();
    if (timeClkNow >= g_timeClkLast) {
        *timeClk32 = (unsigned long)(timeClkNow - g_timeClkLast);
    } else {
        *timeClk32 = (unsigned long)(timeClkNow + (TIMER_MAXLOAD - g_timeClkLast));
    }
    return timeClkNow;
}

extern void os_adjtime(void);
void UpdateSchedClock(void)
{
    unsigned long timeClk32;
    unsigned int intSave;

    intSave = LOS_IntLock();
    g_timeClkLast = UpdateTimeClk(&timeClk32);
    g_schedClockCycle += (unsigned long long)timeClk32;
    g_schedClockNanosec += (unsigned long long)CYCLE_TO_NS(timeClk32);
    (void)os_adjtime();
    (void)LOS_IntRestore(intSave);
}

static unsigned long long GetSchedClock(void)
{
    unsigned long timeClk32;

    UpdateTimeClk(&timeClk32); /*lint !e534*/

    return (g_schedClockNanosec + (unsigned long long)CYCLE_TO_NS(timeClk32));
}

unsigned long long SchedClock(void)
{
    unsigned int intSave;
    unsigned long long timeClk64;

    intSave = LOS_IntLock();
    timeClk64 = GetSchedClock();
    (void)LOS_IntRestore(intSave);

    return timeClk64;
}

unsigned long long hi_sched_clock(void)
{
    unsigned long long c;
    c = SchedClock();
    return c;
}

unsigned long long hi_sched_cycle(void)
{
    unsigned long timeClk32;
    unsigned int intSave;
    unsigned long long cycle;

    intSave = LOS_IntLock();
    UpdateTimeClk(&timeClk32); /*lint !e534*/
    cycle = g_schedClockCycle + (unsigned long long)timeClk32;
    (void)LOS_IntRestore(intSave);

    return cycle;
}

#ifdef LOSCFG_SHELL_EXCINFO

#ifdef LOSCFG_FS_VFS
#include "fs/fs.h"
#endif

VOID LOS_ExcInfoRegHook(UINT32 startAddr, UINT32 space, CHAR *buf, log_read_write_fn hook)
{
    if (hook == NULL || buf == NULL) {
        PRINT_ERR("buf or hook is null\n");
        return;
    }

    g_recordAddr = startAddr;
    g_recordSpace = space;
    g_excInfoBuf = buf;
    g_excInfoRW = hook;

#ifdef LOSCFG_FS_VFS
    los_vfs_init();
#endif
}

VOID OsReadWriteExceptionInfo(UINT32 startAddr, UINT32 space, UINT32 flag, CHAR *buf)
{
    if (buf == NULL || space == 0) {
        PRINT_ERR("buffer is null or space is zero\n");
        return;
    }

#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR
    g_mtdSpinor = get_mtd("spinor");
    if (g_mtdSpinor == NULL) {
        (void)spinor_init();
        g_mtdSpinor = get_mtd("spinor");
        if (g_mtdSpinor == NULL) {
            PRINT_ERR("Init spinor is failed\n");
            return;
        }
    }
#endif

    if (flag == 0) {
#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR
        if (OsWriteExcInfoToSpiFlush(startAddr, space, buf) != LOS_OK) {
            PRINT_ERR("Exception information written to flash failed\n");
        }
        free(buf);  // Consider whether or not  the "buf" is released according to actual use
#endif
    } else if (flag == 1) {
#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR
        if (OsReadExcInfoForSpiFlush(startAddr, space, buf) != LOS_OK) {
            PRINT_ERR("Exception information read from flash failed\n");
        }
#endif
    } else {
        PRINT_ERR("flag is error\n");
    }
}

void OsRecordExcInfoTime(void)
{
#ifdef LOSCFG_FS_VFS
#define NOW_TIME_LENGTH 24
    time_t t;
    struct tm *tmTime;
    char nowTime[NOW_TIME_LENGTH];

    (void)time(&t);
    tmTime = localtime(&t);
    (VOID)memset_s(nowTime, sizeof(nowTime), 0, sizeof(nowTime));
    (void)strftime(nowTime, NOW_TIME_LENGTH, "%Y-%m-%d %H:%M:%S", tmTime); /*lint !e586*/
#undef NOW_TIME_LENGTH
    WriteExcInfoToBuf("%s \n", nowTime);
#endif
}
#endif
